//--------------------------------------------------------------------------------
// Alec G Thilenius
//
// This software is distributed under the MIT License, available at the following
// URL:
// http://www.opensource.org/licenses/mit-license.php
//--------------------------------------------------------------------------------

#include "Graph.h"
using namespace AlecThilenius;


Graph::Graph(void)
{
	m_vertexCount = m_edgeCount = 0;

	if ( !NullVertex )
		NullVertex = VertexPtr(new Vertex ("NULL_REFERENCE_VERTEX", this));

	if ( !NullEdge )
		NullEdge = EdgePtr(new Edge (NullVertex, NullVertex, -1.0f ));
}


Graph::~Graph(void)
{
}

void Graph::Clear()
{
	m_verts.clear();
}

VertexPtr Graph::AddVertex( const std::string& vertexName )
{
	if(m_verts.find(vertexName) == m_verts.end())
	{
		VertexPtr IVP = VertexPtr(new Vertex(vertexName, this));
		m_verts.insert(VertMap::value_type(vertexName, IVP));

		//Save a weak_pointer to IVP in the Vertex for self access
		IVP->m_sharedResource = IVP;

		m_vertexCount ++;
		return IVP;
	}

	return NullVertex;
}

VertexPtr Graph::GetVertex( const std::string& vertexName )
{
	VertMapIttr iter = m_verts.find(vertexName);

	if(iter != m_verts.end())
	{
		return iter->second;
	}

	return NullVertex;
}

bool Graph::RemoveVertex( const std::string& vertexName )
{
	VertMapIttr iter = m_verts.find(vertexName);

	if(iter != m_verts.end())
	{
		//Release edge references from and to Vertex
		iter->second->RemoveAllEdges();

		//Release last graph maintained reference to Vertex
		m_verts.erase(iter);

		m_vertexCount --;
		return true;
	}

	return false;
}

int Graph::VerticieCount() const
{
	return m_vertexCount;
}

EdgePtr Graph::AddEdge( const std::string& fromVertexName, const std::string& toVertexName, double weight )
{
	if ( fromVertexName == toVertexName )
		return NullEdge;

	VertMapIttr iterFrom = m_verts.find(fromVertexName);
	VertMapIttr iterTo = m_verts.find(toVertexName);

	if(iterFrom != m_verts.end() && iterTo != m_verts.end())
	{
		m_edgeCount++;
		return iterFrom->second->CreateEdgeTo(iterTo->second, weight );
	}

	return NullEdge;
}

EdgePtr Graph::GetEdge( const std::string& fromVertexName, const std::string& toVertexName )
{
	VertMapIttr iterFrom = m_verts.find(fromVertexName);
	VertMapIttr iterTo = m_verts.find(toVertexName);

	if(iterFrom != m_verts.end() && iterTo != m_verts.end())
	{
		return iterFrom->second->GetEdgeTo(iterTo->second);
	}

	return NullEdge;
}

bool Graph::RemoveEdge( const std::string& fromVertexName, const std::string& toVertexName )
{
	VertMapIttr iterFrom = m_verts.find(fromVertexName);
	VertMapIttr iterTo = m_verts.find(toVertexName);

	if(iterFrom != m_verts.end() && iterTo != m_verts.end())
	{
		return iterFrom->second->RemoveEdgeTo(iterTo->second);
	}

	return false;
}

int Graph::EdgeCount() const
{
	return m_edgeCount;
}

void AlecThilenius::Graph::Print( std::ostream& oS )
{
	oS << "Vertex Count:" << m_vertexCount << std::endl;
	oS << "Edge Count:" << m_edgeCount << std::endl;

	for ( VertMap::const_iterator iter = m_verts.begin(); iter != m_verts.end(); ++iter )
	{
		oS << *(iter->second) << std::endl;
	}
}

bool AlecThilenius::Graph::Load( std::istream& iS )
{
	if ( !iS.good() )
		return false;

	std::string buffer;
	std::string vertexContext;

	//First pass to parse Vertex
	while ( iS )
	{
		std::getline( iS, buffer );
		if ( buffer.substr(0, 8) == "Vertex: " )
		{
			//Create a vertex, hold context
			vertexContext = buffer.substr(8, buffer.size());
			AddVertex(vertexContext);
		}
	}

	iS.clear();
	iS.seekg(0);

	//Second pass to parse Edges
	while ( iS )
	{
		std::getline( iS, buffer );
		if ( buffer.substr(0, 8) == "Vertex: " )
		{
			vertexContext = buffer.substr(8, buffer.size());
		}
		else if ( buffer.substr(0, 2) == "- " )
		{
			int pos = buffer.find(',');

			std::string left = buffer.substr(2, pos - 2);
			std::string right = buffer.substr(pos + 2, buffer.size());

			double weight = atof(right.c_str());

			//Create a Edge for it.
			AddEdge(vertexContext, left, weight);
		}
	}

	return true;
}

//Stream Overloads
std::ostream& operator << ( std::ostream& stream, Graph& graph )
{
	graph.Print(stream);
	return stream;
}

std::istream& operator >> ( std::istream& stream, Graph& graph )
{
	graph.Load( stream );
	return stream;
}

